using System;
using System.Collections.Generic;
using System.Text;

namespace IGlobalist.Net.RTP
{
    /// <summary>
    /// This class represents RTP data receiver.
    /// </summary>
    public class RTPReceiver
    {
        private RTPSession           m_pSession           = null;
        private string                m_CName              = "";
        private uint                  m_SSRC               = 0;
        private RTPSourceDescription m_pSourceDescription = null;

        /// <summary>
        /// Default constructor.
        /// </summary>
        internal RTPReceiver()
        {
            m_CName = RTPUtils.GenerateCNAME();
            m_SSRC  = RTPUtils.GenerateSSRC();

            m_pSourceDescription = new RTPSourceDescription(m_CName);
        }
        /// <summary>
        /// constructor.
        /// </summary>
        internal RTPReceiver(RTPSession session): this()
        {
            m_pSession = session;
        }

        #region method CreateRtcpCompoundPackets

        /// <summary>
        /// Creates all RTCP report packets what this source(receiver) must send to target.
        /// </summary>
        /// <returns>Returns compound packets.</returns>
        internal RTCPCompoundPacket[] CreateRtcpCompoundPackets()
        {
            /* RFC 3550 6.4.
                Both the SR and RR forms include zero or more reception report
                blocks, one for each of the synchronization sources from which this
                receiver has received RTP data packets since the last report.
                Reports are not issued for contributing sources listed in the CSRC
                list.  Each reception report block provides statistics about the data
                received from the particular source indicated in that block.  Since a
                maximum of 31 reception report blocks will fit in an SR or RR packet,
                additional RR packets SHOULD be stacked after the initial SR or RR
                packet as needed to contain the reception reports for all sources
                heard during the interval since the last report.
            */

            // Create full and minimal SDES.
            RTCPPacketSDES sdes = new RTCPPacketSDES();
            RTCPPacketSDESChunk sdesChunk = new RTCPPacketSDESChunk(m_SSRC,m_CName);
            sdesChunk.Set(m_pSourceDescription);
            sdes.Chunks.Add(sdesChunk);
            RTCPPacketSDES sdesMin = new RTCPPacketSDES();
            sdesMin.Chunks.Add(new RTCPPacketSDESChunk(m_SSRC,m_CName));

            // Check SDES size, if its almost MTU, then use minimal SDES only.
            if(sdes.Size > (m_pSession.MTU * 0.75)){
                sdes = sdesMin;
            }

            // Get all sources for what we need to generate reception report.
            Queue<RTCPPacketSRReportBlock> reportBlocks = new Queue<RTCPPacketSRReportBlock>();
            foreach(RTPReceiveStream stream in m_pSession.ReceiveStreams){
                if(stream.LastRTPPacket > m_pSession.LastRtcpInterval){
                   reportBlocks.Enqueue(stream.ReceptionReport);
                }
            }

            // Create RR packets.
            Queue<RTCPPacketRR> rrPackets = new Queue<RTCPPacketRR>();
            while(reportBlocks.Count > 0){
                RTCPPacketRR rr = new RTCPPacketRR(m_SSRC);
                rrPackets.Enqueue(rr);
                // Add reception blocks up to 31, if more we need crate additional RR packets,
                // this is because RR can hold 31 report blocks only.
                while(rr.ReportBlocks.Count < 31 && reportBlocks.Count > 0){
                    rr.ReportBlocks.Add(reportBlocks.Dequeue());
                }
            }
            
            // Create compound packets. Each compound packet must start with RR and end with SDES.
            List<RTCPCompoundPacket> compPackets = new List<RTCPCompoundPacket>();
            while(rrPackets.Count > 0){                
                RTCPCompoundPacket packet = new RTCPCompoundPacket();
                compPackets.Add(packet);
                // Add RR packets up to MTU - SDES(because we need to add SDES for each comp packet).
                while(rrPackets.Count > 0 && ((packet.TotalSize + rrPackets.Peek().Size) < (m_pSession.MTU - sdes.Size))){
                    packet.Packets.Add(rrPackets.Dequeue());
                }
                                
                // Add full SDES for first packet and later add minimal sdes(CNAME only).
                if(compPackets.Count == 1){
                    packet.Packets.Add(sdes);
                }
                else{
                    packet.Packets.Add(sdesMin);
                    sdes = sdesMin;
                }                
            }

            return compPackets.ToArray();
        }

        #endregion

        

        #region method ResetSSRC

        /// <summary>
        /// Resets SSRC value. Normally this must be done only if SSRC collision.
        /// </summary>
        private void ResetSSRC()
        {
            m_SSRC = RTPUtils.GenerateSSRC();
        }

        #endregion

        // TODO: BYE


        #region Properties Implementation
        
        /// <summary>
        /// Gets source description.
        /// </summary>
        public RTPSourceDescription SourceDescription
        {
            get{ return m_pSourceDescription; }
        }

        /// <summary>
        /// Gets this sender CNAME.
        /// </summary>
        internal string CNAME
        {
            get{ return m_CName; }
        }

        /// <summary>
        /// Gets this sender SSRC value.
        /// </summary>
        internal uint SSRC
        {
            get{ return m_SSRC; }
        }

        #endregion

    }
}
